package com.kd.oss.utils;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.apache.log4j.Logger;

import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.GetObjectRequest;
import com.aliyun.oss.model.ListObjectsRequest;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.OSSObjectSummary;
import com.aliyun.oss.model.ObjectListing;

public class OssUtils {

	private static final Logger logger = Logger.getLogger(OssUtils.class);
	
	private static final String endpoint;
    private static final String accessKeyId;
    private static final String accessKeySecret;
    private static final String bucketName;
    private static OSSClient client = null;
    
    static {
    	Properties ossPro = PropertiesUtils.getProperties("ossfs.properties");
    	endpoint = ossPro.getProperty("oss.endpoint");
    	accessKeyId = ossPro.getProperty("oss.accessKeyId");
    	accessKeySecret = ossPro.getProperty("oss.accessKeySecret");
    	bucketName = ossPro.getProperty("oss.bucketName");
    	client = new OSSClient(endpoint, accessKeyId, accessKeySecret);
    }
    
    public static void reconnect() {
    	close();
    	try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
    	connect();
    	logger.info("Success to reconnect");
    }
    
    public static void connect() {
    	if(null == client) {
    		synchronized(OssUtils.class) {
    			if(null == client) {
    				client = new OSSClient(endpoint, accessKeyId, accessKeySecret);
    			}
    		}
    	}
    	logger.info("Success to connect");
    }
    
    public static void close() {
    	if(null != client) {
    		client.shutdown();
    	}
    	logger.info("Success to shutdown");
    }
    
    public static List<String> list() {
         if (!client.doesBucketExist(bucketName)) {
             throw new RuntimeException("It doesn't exist this bucket[" + bucketName + "]");
         }
         try {
	         // 使用默认参数获取存储空间的文件列表，默认最多返回100条
	         ObjectListing objectListing = client.listObjects(bucketName);
	         List<OSSObjectSummary> sums = objectListing.getObjectSummaries();
	         List<String> keys = new ArrayList<String>();
	         for (OSSObjectSummary s : sums) {
	             System.out.println("\t" + s.getKey());
	             if(!s.getKey().endsWith(".txt")) {
	            	 continue ;
	             }
	             keys.add(s.getKey());
	         }
	         return keys;
        } catch (OSSException e) {
 			logger.error("Failed to list files meta", e);
 		} catch (ClientException e) {
 			logger.error("Failed to list files meta", e);
 		}
        return null;
    }
	
    /**
     * 从指定的某Object后返回，默认最多100条
     * @param marker
     */
    public static List<String> list(String marker) {
    	if (!client.doesBucketExist(bucketName)) {
            throw new RuntimeException("It doesn't exist this bucket[" + bucketName + "]");
        }
    	try {
	    	// 从指定的某Object后返回，默认最多100条
	    	ObjectListing objectListing = client.listObjects(new ListObjectsRequest(bucketName).withMarker(marker));
	    	List<OSSObjectSummary> sums = objectListing.getObjectSummaries();
	    	List<String> keys = new ArrayList<String>();
	    	for (OSSObjectSummary s : sums) {
	            System.out.println("\t" + s.getKey());
	            if(!s.getKey().endsWith(".txt")) {
	           	 continue ;
	            }
	            keys.add(s.getKey());
	        }
	        return keys;
    	} catch (OSSException e) {
			logger.error("Failed to list files meta", e);
		} catch (ClientException e) {
			logger.error("Failed to list files meta", e);
		}
    	return null;
    }
    
    /**
     * 分页获取所有Object，每页maxKeys条Object
     * @param market
     * @param maxKeys
     * @return
     */
    public static List<String> list(String marker, int maxKeys) {
    	if (!client.doesBucketExist(bucketName)) {
            throw new RuntimeException("It doesn't exist this bucket[" + bucketName + "]");
        }
        // 分页获取所有Object，每页maxKeys条Object
    	try {
	    	ObjectListing objectListing = client.listObjects(new ListObjectsRequest(bucketName).withMarker(marker).withMaxKeys(maxKeys));
	    	if(objectListing.isTruncated()) {
		    	List<OSSObjectSummary> sums = objectListing.getObjectSummaries();
		    	List<String> keys = new ArrayList<String>();
		    	for (OSSObjectSummary s : sums) {
		//            System.out.println("\t" + s.getKey());
		            if(!s.getKey().endsWith(".txt")) {
		           	 	continue ;
		            }
		            keys.add(s.getKey());
		        }
		        return keys;
	    	}
	    	return null;
    	} catch (OSSException e) {
			logger.error("Failed to list files meta", e);
		} catch (ClientException e) {
			logger.error("Failed to list files meta", e);
		}
    	return null;
    }
    
    public static ObjectListing objectListing(String marker, int maxKeys) {
    	try {
    		return client.listObjects(new ListObjectsRequest(bucketName).withMarker(marker).withMaxKeys(maxKeys));
    	} catch (OSSException e) {
			logger.error("Failed to list files meta", e);
		} catch (ClientException e) {
			logger.error("Failed to list files meta", e);
		}
    	return null;
    }
    
    /**
     * Download an object from your bucket
     */
    public static boolean download(String key, String localDir) {
//        System.out.println("Downloading an object");
        try {
	    	OSSObject object = client.getObject(new GetObjectRequest(bucketName, key));
	        System.out.println("Content-Type: "  + object.getObjectMetadata().getContentType());
			return save(object, localDir, "UTF-8");
        } catch (OSSException e) {
			logger.error("Failed to download some files", e);
			OssUtils.reconnect();
		} catch (ClientException e) {
			logger.error("Failed to download some files", e);
			OssUtils.reconnect();
		}
        return false;
    }
    /**
     * Download an object from your bucket
     */
    public static boolean download(String key, String localDir, boolean isConnectable) {
//        System.out.println("Downloading an object");
        try {
	    	OSSObject object = client.getObject(new GetObjectRequest(bucketName, key));
	        System.out.println("Content-Type: "  + object.getObjectMetadata().getContentType());
			return save(object, localDir, "UTF-8");
        } catch (OSSException e) {
			logger.error("Failed to download some files", e);
			if(isConnectable) {
				OssUtils.reconnect();
			}
		} catch (ClientException e) {
			logger.error("Failed to download some files", e);
			if(isConnectable) {
				OssUtils.reconnect();
			}
		}
        return false;
    }
    
    /**
     * save a file
     * @param object
     * @param localDir
     * @param encoding
     * @return
     */
    private static boolean save(final OSSObject object, final String localDir, String encoding) {
    	String key = object.getKey();
    	String[] subKeys = null;
    	if(key.contains("/")) {
    		subKeys = key.split("/");
    	}
    	if(null == subKeys) {
    		return false;
    	}
    	boolean success = false;
    	String dir = "";
    	if(localDir.endsWith("/")) {
    		dir = localDir.substring(0, localDir.length()-1);
    	}
    	String path = dir + File.separator;
    	for(String subKey : subKeys) {
    		if(subKey.endsWith(".txt")) {
    			path += File.separator + subKey;
    			continue ;
    		}
    		path += subKey;
    		File file = new File(path);
    		if(!file.exists()) {
    			file.mkdir();
    		}
    	}
    	BufferedWriter writer = null;
        BufferedReader reader = null;
        try {
        	writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path), encoding));
        	reader = new BufferedReader(new InputStreamReader(object.getObjectContent(), encoding));
        	String line = null;
			while (null != (line=reader.readLine())) {
	            writer.write(line);
	            writer.newLine();
	        }
			writer.flush();
			success = true;
        } catch (IOException e) {
        	e.printStackTrace();
        	success = false;
        } finally {
        	if(null != reader) {
        		try {
					reader.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
        	}
        	if(null != writer) {
        		try {
					writer.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
        	}
        }
        return success;
    }
}
